Skip to content

feat: add mask() method to input field components for masking library integration#161

Draft
Copilot wants to merge 10 commits into
mainfrom
copilot/add-mask-method-to-field-components
Draft

feat: add mask() method to input field components for masking library integration#161
Copilot wants to merge 10 commits into
mainfrom
copilot/add-mask-method-to-field-components

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 17, 2026

Summary

Adds a public .mask(callback) method to SmarkForm field components that provides a clean integration scaffold for input-masking libraries (like iMask.js) without adding any hard dependency.

Changes

src/types/input.type.js

mask(callback) method (added to the input class, inherited by number, date, time, datetime-local, color, etc.):

  • Temporarily changes <input type> to "text" for any non-text input, because masking libraries like iMask.js require type="text". The original type is preserved in _originalType.
  • Calls callback(targetFieldNode) with the raw DOM element as the sole argument — this is the integration point where the caller attaches iMask or any other library.
  • Stores the callback's return value as _maskInstance.
  • For singletons, delegates to the inner field component (so _maskInstance lives where export() reads it).
  • Returns this for chaining.

_setTargetFieldValue() updated:

  • Dispatches a synthetic input event on the element when a mask instance is active. This allows masking libraries to re-process the value when import() is called programmatically.

export() updated:

  • When _maskInstance.unmaskedValue is defined, uses it as the raw value instead of nodeFld.value. This ensures that derived types (number, date, time, etc.) receive the raw unmasked string and can still apply their own semantic type conversion (e.g. Number("1234.56")1234.56 rather than Number("1,234.56")NaN).

test/mask.tests.js (new)

13 Playwright test cases covering:

  • Type change for number/date inputs; no-op for text inputs
  • _originalType is stored correctly
  • Callback receives the correct DOM element
  • mask() returns this for chaining
  • export() uses unmaskedValue when provided
  • Number fields still export a JS number after masking
  • Fallback to nodeFld.value when mask has no unmaskedValue (or returns null)
  • Singleton delegation: _maskInstance lands on the inner field
  • import() dispatches input event on masked fields (and does NOT dispatch it on unmasked fields)

Usage example (iMask.js)

const form = new SmarkForm(document.querySelector("body"));
await form.rendered;

const priceField = form.find("/price");
priceField.mask(node => IMask(node, {
    mask: Number,
    scale: 2,
    thousandsSeparator: ',',
    radix: '.',
}));
// form.export() will still return { price: 1234.56 } even though
// the visible value is "1,234.56"

Testing

All 284 existing tests continue to pass. All 13 new mask tests pass.

… integration

- Add mask(callback) method to SmarkField (via input.type.js):
  - Changes <input> type to "text" for non-text inputs (required by iMask.js etc.)
  - Stores original type in _originalType for reference
  - Calls callback(targetFieldNode) and stores result as _maskInstance
  - Delegates to inner field for singletons, returns `me` for chaining
- Update _setTargetFieldValue() to dispatch "input" event when mask is active,
  so masking libraries re-process the new value on programmatic import()
- Update export() to use _maskInstance.unmaskedValue when available,
  so derived types (number, date, etc.) still parse/return the correct semantic type
- Add comprehensive tests in test/mask.tests.js (13 scenarios)

Agent-Logs-Url: https://github.com/bitifet/SmarkForm/sessions/72c9bcc2-72c5-42a0-8d32-9fe27015b3da

Co-authored-by: bitifet <1643647+bitifet@users.noreply.github.com>
bitifet added 9 commits April 24, 2026 12:16
- Primary credit card example showing space-separated 4-digit groups
- Price example demonstrating number field type with decimal/thousands separators
- Singleton masking pattern with inner field delegation
- _originalType restoration for native HTML5 input behavior
- Validation integration using mask library events
- Updated AGENTS.md with masking feature details
…sages

- Add deep isSerializable() validation in setNodeOptions that checks for
  functions, symbols, undefined, non-finite numbers, and circular references
- Throw renderError(INVALID_OPTIONS_OBJECT) instead of cryptic JSON.stringify errors
- Convert field_masking.md static code blocks to interactive sampletabs playgrounds
- Restore original general tests (document loaded, focus behavior,
  default values focus race, basic introspection)
- Add new tests for options serialization validation (cyclic refs, functions)
- These validate the INVALID_OPTIONS_OBJECT renderError behavior
- Remove 'undefined' check from isSerializable() — JSON.stringify
  silently drops undefined property values, so they are serializable
- Guard getPath() against unset parents (called during construction
  when renderError is thrown before parents is initialized)
- Update masking docs: replace 'restores original type' with
  'masking is permanent' to match current implementation
…try/catch

- Resolve string selectors (e.g., '#payment') to DOM nodes in the
  SmarkForm constructor so string-based instantiation works
- Remove the debugging try/catch wrapper from setNodeOptions —
  the isSerializable pre-check throws clear errors that propagate
  naturally through the constructor
- Update validation tests to match new error behavior
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants